Multithreaded Rendering & Graphics Jobs

版本检查: 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.

Singlethreaded Rendering (single client, no worker thread)

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 as well. This is suboptimal as all commands you execute on the main thread subtract from important frametime which you could have used for other subsystems running on the main thread.

Multithreaded Rendering (single client, single worker 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, GLES, etc. inherit from the GfxDevice.

Renderthread

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 known as 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 render thread - 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).

Availability

Multithreaded Rendering is enabled or disabled conditionally depending on the graphics API and target platform. For instance, 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 are able to enable or disable Multithreaded Rendering on.

Graphics API iOS Android Desktop
OpenGLES 2/3 Always off Configurable N/A
Metal Configurable N/A Configurable
Vulkan N/A Configurable Configurable

Performance Consideration

You should enable Multithreaded Rendering whenever possible as it usually benefits performance greatly. Tip: You should profile the usage of Multithreaded Rendering, however, on very low-end devices there might be little to no benefit.

Profiling Multithreaded Rendering

Often you want to profile Multithreaded Rendering to improve rendering performance and it’s necessary to disable Multithreaded Rendering to get correct results. There’s a script-only player setting to change Multithreaded Rendering. Alternatively, disable this in the Player Settings of the relevant platforms. 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, choose -force-gfx-st instead.

Jobified Rendering (multiple clients, single worker thread)

This render mode was available in Unity 5.4 to Unity 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 may 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 are then executed on the worker thread.

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.

Graphics Jobs (multiple clients, no worker thread)

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 cost 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 translates 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.

Availability

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.

Graphics API iOS Android Desktop
OpenGLES 2/3 Not Supported Not Supported N/A
Metal N/A N/A N/A
Vulkan N/A Configurable Configurable

Profiling Rendering

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 will have different timing as the overhead of managing the other modes doesn’t appear in the profiler.

GfxThreadableDevice Functions

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 thread-safe data. 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, instrument the SetShadersThreadable() method to gain information on how long it takes to set actual shaders and compare them to their non-threaded equivalent.