The Drag[en]gine Game Engine provides support to modules and to some degreee game scripts the possiblity to run tasks parallel to the main thread. No external libraries are used for this so it is fully platform agnostic and does not rely on the precense of specific hardware nor limiting the game developer with convoluted logic analytics and conditions. Game script usually do not use parallel processing since most script language are not suited for running in threaded environments. If script modules support this though parallel processing is a viable solution. Although not directly using parallel processing it is good for a game developer to have an understanding on how it works to avoid playing tricks on the engine modules potentially impacting performance. The image below shows a small time slice during game frame update.
Parallel processing is implemented using a basic task processing system with a set of worker threads. Whenever a task is placed to be processed a free worker threads picks it up and starts processing it right away. This part of the task processing is the asynchronous part. This part of the task is run in parallel to the main thread and does operate only on data located in the task itself. Once finished the task is placed in the list of finished tasks. A finished task is not done yet. It has simply finished its asynchronous part. At various places during game frame update processing the parallel task manager can finalize outstanding finished tasks. This is done automatically during certain calls. During this time finished tasks apply their results to the game state. Since this is done synchronously during the main thread they can access all the game state safely. This avoids the need to implement complex mutex locking schemes to properly protect data access all over the place.
Most of the time you do not get to see all this taking place. There are though situations possible where game scripts can hamper the parallel processing. An example is the animator instance processing. Once the animator instance controllers are configurated the calculation of the animation state is started. The calculation is added as a parallel task. In general the task is processed in the near future and applied to the respective component once finalized. This is the optimal case. Now it is possible the game scripts try to access game states which rely on the animation state to be calculated already. In this situation the parallel processing manager is forced to wait for the parallel task calculating the animation state to be finished before continuing the main thread. This can produces a small wait if the timing is bad. The game engine ensures the task is finished before being used. It can though not guarantee optimal performance if the task result is requested too early. For this reason it is a good practice to not access data calculated with parallel processing too early after producing the task to avoid wasting time waiting for the task to be finished. Functions with such caveats exist only a few and are marked accordingly.
Parallel tasks support simple dependencies. A task can depend on another task to be finished before being accepted to run. This can be used to order certain tasks to force them to run in a specific order to obtain correct results. A typical example is again the animator instance processing. If multiple animator instances are applied to the same component the individual tasks for calculating the animation states are chained using task dependencies. Here too the game developer usually does not notice this process since the game engine takes care of properly processing tasks before their results are used.
Certain types of modules take advantage of using a permanent thread on ther own. Graphic modules are known to use render threads as optimization means. The parallel task system is geared for being manipulated from the main thread. An asynchronous thread is not allowed to use the parallel task manager in the exact same way the main thread is allowed to. While adding tasks works the same for both the asynchronous thread is not allowed to trigger the finilizing of finished threads. Doing so would damage game state and in the worst case cause a segmentation fault. Parallel tasks originating from pure threads have to use only the parallel processing part. The finalize part has to be empty. The tasks have to update the thread state by using mutex locking once finished since they can not be finalized. If done this way asynchronous threads can still use parallel tasks like the main thread does in a safe way.