Multithreaded Rendering & Graphics Jobs
Checked with version: 2018.1
Unity supports several modes of rendering depending on platform availability and graphics API:
If you do not select one of these modes in the Player Settings, Unity uses singlethreaded rendering.
Unity uses singlethreaded rendering by default if none of the other modes are enabled.
This causes the single client to occupy the main thread while executing the high-level rendering commands.
The single client executes all the rendering commands (RCMD) on the main thread. The client also owns the real graphics device GfxDevice and performs the actual rendering through the underlying graphics API (GCMD) on the main thread. This is suboptimal, because all commands you execute on the main thread subtract from important frametime which you could use for other subsystems running on the main thread.
Unity enables Multithreaded Rendering by default if the graphics API permits it. To disable Multithreaded Rendering (generally for profiling purposes), go to the Player Settings (menu: Edit > Project Settings > Player) window, scroll down and uncheck the Multithreaded Rendering checkbox.
Multithreaded rendering in Unity is implemented as a single client, single worker thread. This works by taking advantage of the abstract GfxDevice interface in Unity. The different graphics API implementations, (such as Vulkan, Metal, and GLES) inherit from the GfxDevice.
When you enable multithreaded rendering you can spot the GfxDeviceClient class functions in call-stacks on a native platform profiler such as XCode. In the Unity Timeline Profiler, it is called the Renderthread.
The high-level rendering code of the client, which executes on the main thread, uses the renderthread.
The single client forwards all the rendering commands (RCMD) to the renderthread - a special worker thread only for rendering - which owns the real graphics device GfxDevice and performs the actual rendering through the underlying graphics API (GCMD).
Unity enables or disables Multithreaded Rendering conditionally, depending on the graphics API and target platform. For example, on iOS, Multithreaded Rendering is always enabled when running under Metal, but OpenGLES 2.0/3.0 does not support Multithreaded Rendering and Unity executes everything on the main thread only. The following table provides an overview of what platforms and Graphics API you can enable or disable Multithreaded Rendering.
|OpenGLES 2/3||Always off||Configurable||N/A|
You should enable Multithreaded Rendering whenever possible, as it usually benefits performance greatly. Tip: You should also profile the use of Multithreaded Rendering, and be aware that on very low-end devices there might be little to no benefit.
Often, you need to profile Multithreaded Rendering to improve rendering performance, and it’s necessary to disable the Multithreaded Rendering setting to get correct results (see the later section on Profiling Rendering). You can also use the script-only player setting PlayerSettings.MTRendering to change Multithreaded Rendering. Alternatively, disable this in the Player Settings of the relevant platforms (see the earlier section on Availability). To disable Multithreaded Rendering in the Editor, use the following command line option: -force-gfx-direct. If you need the client device enabled (for example, to use display lists) use -force-gfx-st instead.
This render mode was available in Unity 5.4, 5.5 and 5.6, but has since been replaced by Graphics Jobs.
Multiple jobs, each of them running on its own thread, generate intermediate graphics commands (IGCMD). Afterwards, similar to Multithreaded Rendering (single client, single worker thread), a worker thread processes the buffered intermediate graphics commands and submits graphics commands (GCMD) to the real graphics device GfxDevice.
These jobs have clearly defined inputs (RCMD) because they can run at the same time as user script code, which potentially changes the state of any object in the world. Jobs output commands (RCMD) to a different GfxDeviceClient per thread, and they write into their own block-allocating buffers, which the worker thread then executes.
Note: The worker thread does not wait until a job finishes before it starts executing its commands (IGCMD), but it always executes them in the same order they are scheduled.
Unity disables Graphics Jobs by default, but you can enable them in the Player Settings. Multiple native command generation threads take advantage of the graphics APIs that support recording graphics commands (GCMD) in a native format on multiple threads. This removes the performance impact of writing and reading commands in a custom format before submitting them to the API. Similar to the other modes, Graphics Jobs generate commands by calling GfxDevice functions. However, since the devices are now platform-specific, Graphics Jobs translate the commands directly into, for example DirectX 12 or Vulkan command buffers.
Note: Currently, Graphics Jobs do not have a renderthread to schedule jobs, causing a small amount of overhead on the main thread for scheduling.
Note: GPU profiling is automatically disabled when you enable Graphics Jobs.
Graphics Jobs are available depending on the graphics API and target platform. The following table gives an overview of the availability of Graphic Jobs on each platform and Graphics API.
|OpenGLES 2/3||Not Supported||Not Supported||N/A|
When you investigate the rendering system while profiling, disable Multithreaded Rendering, Jobified Jobs, and Graphics Jobs to see the whole render queue executed on the main thread in singlethreaded rendering mode. This makes it easier to measure the timing and see the command queue easier.
Note: When you run in singlethreaded rendering mode to execute everything on the main thread, you get different timing, because the overhead of managing the other modes doesn’t appear in the profiler.
When you look at GfxDeviceClient functions in a native call stack while profiling, it often adds extra virtual functions from the GfxThreadableDevices class.
These extra functions are variations of the GfxDevice functions that take data that isn’t thread-safe (for example, ShaderLab::PropertySheet) and convert them to data that is thread-safe. When you call SetShaders() in Multithreaded Rendering, the main thread takes a ShaderLab::PropertySheet and turns it into plain serialized data that GfxDevice feeds to SetShadersThreadable() on the renderthread. When you investigate shader performance, measure the timing of the SetShadersThreadable() method to gain information on how long it takes to set actual shaders and compare them to their non-threaded equivalent.