Multi-threaded architecture in UE4

In UE4 the architecture runs parts of the engine differently to previous versions. The system has three unique threads that run one after another in separate frames to draw objects and items onto the screen. These three threads are referred to as the Game thread, which is the current frame this is being displayed, the Draw thread, in which the thread calculates what requires rendering, and lastly the Render thread, in which geometry begins the rendering process (Edmonds 2018).

The most common constraint of multi-threading in any language or game engine is how data values are shared between threads. As stated above, Unreal Engine uses C++ as the language foundation. Over the years C++ has recognised issues that can stem from multi-threading and have undertaken steps to create solutions to help programmers with issues that may arise (Grigoryan & Wu 2020). The most common issues encountered in multi-threading are as follows:

A race condition: This is caused by two or more threads that are attempting to modify the use of an object or piece of memory. As defined by the C++ Library and International Organization for Standardization (ISO), if data race exists and has the ability to write on the object or memory to change its value, it is not only seen as undefined behaviour, but also an unpredictable outcome (Standard C++ Foundation 2020).

A deadlock: One of the causes of deadlock is having two threads that are not able to complete a task as the object or memory that is being held by another thread is locked to it. This is the result of introducing a locking of a thread through design and the mutex library. Deadlocks can also occur without locking an object with the mutex class. For example, when a thread is completing a task and a second thread is waiting for the first to finish (Williams 2012).

Within the unreal engine, and C++, dealing with multi-threading environments can be a gruelling task as attempting to find bugs or issues within data races or deadlocks can sometimes require the understanding of low-level languages. Trying to reproduce these issues can sometimes take long periods of time, and even longer periods of time to rectify (Ljumovic 2014).

Further, a more subtle issue that can occur in multi-threading is a performance-based issue called context switching. This occurs in multi-threading through the creation and subsequent deletion of threads once the task is completed, or when a thread or processor swaps to another thread or processor. Due to these tasks always being a constant, they are classed as a direct overhead cost. The secondary cost is classed as an indirect cost, which cannot be calculated as some attributes have undetermined characteristics such as the locating of memory blocks, waiting for executions and many other factors (Ding & Shen 2007).

References:

Edmonds, M 2018, Mastering game development with Unreal Engine 4 – Second edition, Packt Publishing.

Grigoryan, V & Wu, S 2020, Expert C++, Packt Publishing.

Li, C, Ding, C & Shen, K 2007, ‘Quantifying the cost of context switch, Proceedings of the 2007 workshop on Experimental computer science, pp. 1-4.

Ljumovic, M 2014, C++ Multithreading cookbook, Packt Publishing.

Standard C++ Foundation 2020, C++ Standard Library Extensions – Concurrency, accessed 22 May 2020, https://isocpp.org/wiki/faq/cpp11-library-concurrency

Williams, A 2012, C++ concurrency in action: Practical multithreading, Manning Publications.

Leave a comment