Asynchronous Simulation: Difference between revisions
m →Spring Multithreaded Lua Models: cleanup |
rearrange topics |
||
| Line 1: | Line 1: | ||
== Spring Multithreaded Lua Models== | == Spring Multithreaded Lua Compatibility == | ||
In order to make your Lua code compatible with the multithreaded engine, you need to follow certain rules. The rules apply to any code run by a rendering thread, which means all DrawXXX call-ins plus the Update call-in. UI call-ins (mouse/keyboard etc) are also run by the rendering thread. | |||
== All versions == | |||
Lua code must be designed so that it works even if objects are deleted (and possibly replaced by another object with same ID) in between '''any''' rendering call-ins. This means it is not recommended for rendering code to store lists of e.g. units in global variables, and if you still want to do that, each one must be re-checked for validity before use. Keep in mind that if an object has been replaced, the operation that you intended to perform may no longer be valid for the new object. | |||
== 0.82.X == | |||
Unfortunately, Spring MT has very limited Lua rendering support in this engine version, and it carries a huge negative performance impact. Especially time-consuming rendering such as looping through a set of units and drawing something for each one, must be avoided. | |||
Only the rendering thread may use Lua OpenGL stuff, so you must be careful not to create e.g. display lists in the wrong call-ins. | |||
There are also numerous related bugs in this version, that could turn your coding job into a trial-and-error nightmare. Deadlocks and crashes can be expected. | |||
== 83.0+ == | |||
Rendering in a gadget can - depending on the threading model (see below) - negatively affect the performance, although the performance penalty is less severe compared to in the previous engine version. Avoid writing any time-consuming rendering code in a gadget. The easy way out is to let a widget do the rendering instead. The gadget can send a message to the widget by calling Script.LuaUI.XXX(), but the value returned from this call is undefined in MT Spring. | |||
== Lua Threading Models== | |||
These modes can be set for a game using <code>luaThreadingModel</code> in [[Modrules.lua#system|Modrules.lua]] | These modes can be set for a game using <code>luaThreadingModel</code> in [[Modrules.lua#system|Modrules.lua]] | ||
| Line 21: | Line 37: | ||
:<code>6</code> - Dual states for all, all synced/unsynced communication (widgets included) is unmanaged and via SendToUnsynced only. | :<code>6</code> - Dual states for all, all synced/unsynced communication (widgets included) is unmanaged and via SendToUnsynced only. | ||
::''Same as mode(5) but the SendToUnsynced and Script.LuaUI.XXX() communication is unmanaged. The Lua coder must manually check if object references sent are still valid when the message arrives.'' | ::''Same as mode(5) but the SendToUnsynced and Script.LuaUI.XXX() communication is unmanaged. The Lua coder must manually check if object references sent are still valid when the message arrives.'' | ||
:'''Which mode to use?''' The default mode(2) is usually good enough unless you need (execution time consuming) rendering gadgets. If you write a new widget/gadget, consider designing it to be compatible with a higher mode. | :'''Which mode to use?''' The default mode(2) is usually good enough unless you need (execution time consuming) rendering gadgets. If you write a new widget/gadget, consider designing it to be compatible with a higher mode. | ||
Revision as of 01:04, 5 April 2012
Spring Multithreaded Lua Compatibility
In order to make your Lua code compatible with the multithreaded engine, you need to follow certain rules. The rules apply to any code run by a rendering thread, which means all DrawXXX call-ins plus the Update call-in. UI call-ins (mouse/keyboard etc) are also run by the rendering thread.
All versions
Lua code must be designed so that it works even if objects are deleted (and possibly replaced by another object with same ID) in between any rendering call-ins. This means it is not recommended for rendering code to store lists of e.g. units in global variables, and if you still want to do that, each one must be re-checked for validity before use. Keep in mind that if an object has been replaced, the operation that you intended to perform may no longer be valid for the new object.
0.82.X
Unfortunately, Spring MT has very limited Lua rendering support in this engine version, and it carries a huge negative performance impact. Especially time-consuming rendering such as looping through a set of units and drawing something for each one, must be avoided.
Only the rendering thread may use Lua OpenGL stuff, so you must be careful not to create e.g. display lists in the wrong call-ins.
There are also numerous related bugs in this version, that could turn your coding job into a trial-and-error nightmare. Deadlocks and crashes can be expected.
83.0+
Rendering in a gadget can - depending on the threading model (see below) - negatively affect the performance, although the performance penalty is less severe compared to in the previous engine version. Avoid writing any time-consuming rendering code in a gadget. The easy way out is to let a widget do the rendering instead. The gadget can send a message to the widget by calling Script.LuaUI.XXX(), but the value returned from this call is undefined in MT Spring.
Lua Threading Models
These modes can be set for a game using luaThreadingModel in Modrules.lua
0- No MT, single threaded;1- Single state;- This mode has a single Lua environment for everything. It will cause a significant slowdown because simulation and rendering threads block each other trying to access the shared Lua environment. Especially problematic is Lua rendering; being inherently time consuming (looping through large amounts of units etc.) it will force the simulation thread to wait. This gives rise to the classic phenomenon of having incredibly high FPS but still lagging behind (high ping) since simulation cannot keep up with the normal pace.
2- Single state, batching of unsynced events;- This mode is considerably better than mode(1) since it has a separate Lua environment for LuaUI.
- All simulation events sent to LuaUI are also batched/delayed to reduce the need for the threads to block each other. For instance, if simulation triggers UnitMoved(), the event will not reach LuaUI right away, but is put in a batch and handled as soon as possible by the rendering thread. The batching system is managed, i.e. if the unit that triggered UnitMoved is about to be deleted (and thus invalidated) the batch is forcefully run before deletion occurs. Because the forced batch run is performed by the simulation thread, locking is required and this can degrade simulation performance for the same reasons as in mode(1).
- The other means of unsynced communication, SendToUnsynced and Script.LuaUI.XXX() are also batched and managed to make sure any object ID or such sent are still valid once the message arrives. Note: Script.LuaUI.XXX() does because of the batching NOT have a return value.
- This mode unfortunately suffers the same problems as mode(1) for rendering gadgets, since simulation events must be sent to the gadgets directly (without batching) in order not to desync the game.
3- Dual states for synced, batching of unsynced events, synced/unsynced communication via EXPORT table and SendToUnsynced;- Same as mode(2), but the gadgets have separate Lua environments depending on whether the simulation or rendering thread is invoking it. This alleviates the negative performance impact for rendering gadgets, but makes it impossible to directly share synced gadget data to the unsynced part via _G --> SYNCED. To work around this limitation, this mode introduces _G.EXPORT that is automatically copied to SYNCED.EXPORT. Only small amounts of data should be stored in the EXPORT table, because the copying is performed very frequently. Cyclical and/or very deeply nested tables are not allowed. Larger data can be stored temporarily, e.g.
- _G.EXPORT.myhugetable = huge_table
- SendToUnsynced("huge_table_available")
- _G.EXPORT.myhugetable = nil
4- Dual states for synced, batching of unsynced events, synced/unsynced communication via SendToUnsynced only;- Same as mode(3) but with the EXPORT table disabled to increase the performance further. You don't need EXPORT, SendToUnsynced alone is sufficient.
5- Dual states for all, all synced/unsynced communication (widgets included) via SendToUnsynced only;- Same as mode(4), but adds separate Lua environments for LuaUI depending on whether the simulation or rendering thread is invoking it. With dual LuaUI states, batching is no longer needed and therefore disabled. This makes it even faster, since batching overhead is eliminated and forced batch runs (see mode(2)) are not needed. However, this mode adds significant complexity for the programmer, since essentially two instances of the same widget are run, and only the calling thread determines which one is invoked. As a rule of thumb, all simulation events (GameFrame etc.) are invoked by the simulation thread. All UI and rendering events and Update() are invoked by the rendering thread. So, if a variable is modified in GameFrame(), that change cannot be seen in Update(). The widget must instead use SendToUnsynced to communicate any data from the simulation widget instance to the rendering widget instance. Design wise it may be suitable to write the widget similarly to a gadget, with clearly separated "synced" and "unsynced" parts, each with their own variables, functions and call-ins.
6- Dual states for all, all synced/unsynced communication (widgets included) is unmanaged and via SendToUnsynced only.- Same as mode(5) but the SendToUnsynced and Script.LuaUI.XXX() communication is unmanaged. The Lua coder must manually check if object references sent are still valid when the message arrives.
- Which mode to use? The default mode(2) is usually good enough unless you need (execution time consuming) rendering gadgets. If you write a new widget/gadget, consider designing it to be compatible with a higher mode.