FX.AllowAsyncTick

FX.AllowAsyncTick

#Overview

name: FX.AllowAsyncTick

This variable is created as a Console Variable (cvar).

It is referenced in 6 C++ source files.

#Summary

#Usage in the C++ source code

The purpose of FX.AllowAsyncTick is to control whether particle systems in Unreal Engine 5 can be ticked (updated) asynchronously in parallel. This setting is primarily used for the particle system and effects (FX) subsystem of the engine.

The Unreal Engine subsystem that relies on this setting variable is the particle system, specifically within the Engine module. It’s used in the FXSystem, ParticleComponents, and ParticleSystemManager.

The value of this variable is set through a console variable (CVar) named “FX.AllowAsyncTick”. It’s associated with the bAllowAsyncTick variable, which is initialized to !WITH_EDITOR, meaning it’s enabled by default in non-editor builds.

This variable interacts with several other variables and conditions:

  1. FXConsoleVariables::bFreezeParticleSimulation
  2. FApp::ShouldUseThreadingForPerformance()
  3. GDistributionType

Developers must be aware of the following when using this variable:

  1. It affects performance, as it determines whether particle systems can be updated in parallel.
  2. It’s disabled by default in the editor, which may lead to different behavior between editor and runtime.
  3. It’s part of a larger set of conditions that determine whether async ticking is allowed.

Best practices when using this variable include:

  1. Consider the impact on performance and behavior when enabling or disabling it.
  2. Test thoroughly in both editor and runtime environments, as the default values differ.
  3. Be aware of its interaction with other variables and settings that affect particle system behavior.

Regarding the associated variable bAllowAsyncTick:

#References in C++ code

#Callsites

This variable is referenced in the following C++ source code:

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Particles/FXSystem.cpp:164

Scope (from outer to inner):

file
namespace    FXConsoleVariables

Source code excerpt:

		);
	FAutoConsoleVariableRef CVarAllowAsyncTick(
		TEXT("FX.AllowAsyncTick"),
		bAllowAsyncTick,
		TEXT("allow parallel ticking of particle systems."),
		ECVF_Default
		);
	FAutoConsoleVariableRef CVarParticleSlackGPU(
		TEXT("FX.ParticleSlackGPU"),

#Associated Variable and Callsites

This variable is associated with another variable named bAllowAsyncTick. They share the same value. See the following C++ source code.

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Particles/FXSystem.cpp:126

Scope (from outer to inner):

file
namespace    FXConsoleVariables

Source code excerpt:

	int32 bFreezeGPUSimulation = false;
	int32 bFreezeParticleSimulation = false;
	int32 bAllowAsyncTick = !WITH_EDITOR;
	float ParticleSlackGPU = 0.02f;
	int32 MaxParticleTilePreAllocation = 100;
	int32 MaxCPUParticlesPerEmitter = 1000;
	int32 MaxGPUParticlesSpawnedPerFrame = 1024 * 1024;
	int32 GPUSpawnWarningThreshold = 20000;
	float GPUCollisionDepthBounds = 500.0f;

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Particles/FXSystem.cpp:165

Scope (from outer to inner):

file
namespace    FXConsoleVariables

Source code excerpt:

	FAutoConsoleVariableRef CVarAllowAsyncTick(
		TEXT("FX.AllowAsyncTick"),
		bAllowAsyncTick,
		TEXT("allow parallel ticking of particle systems."),
		ECVF_Default
		);
	FAutoConsoleVariableRef CVarParticleSlackGPU(
		TEXT("FX.ParticleSlackGPU"),
		ParticleSlackGPU,

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Particles/ParticleComponents.cpp:5545

Scope (from outer to inner):

file
function     void UParticleSystemComponent::TickComponent

Source code excerpt:

	if (!IsTickManaged() || bWarmingUp)
	{
		if (!ThisTickFunction || !ThisTickFunction->IsCompletionHandleValid() || !CanTickInAnyThread() || FXConsoleVariables::bFreezeParticleSimulation || !FXConsoleVariables::bAllowAsyncTick || !FApp::ShouldUseThreadingForPerformance() ||
			GDistributionType == 0) // this may not be absolutely required, however if you are using distributions it will be glacial anyway. If you want to get rid of this, note that some modules use this indirectly as their criteria for CanTickInAnyThread
		{
			bDisallowAsync = true;
		}

		if (bDisallowAsync)

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Particles/ParticleSystemManager.cpp:742

Scope (from outer to inner):

file
function     void FParticleSystemWorldManager::Tick

Source code excerpt:

	//UE_LOG(LogParticles, Warning, TEXT("| Queue Concurrent Ticks | TG: %s |"), *TickGroupEnum->GetNameByValue(TickGroup).ToString());

	bool bAllowTickConcurrent = !FXConsoleVariables::bFreezeParticleSimulation && FXConsoleVariables::bAllowAsyncTick && FApp::ShouldUseThreadingForPerformance() && GDistributionType != 0;
	if (bAllowTickConcurrent)
	{
		//TODO: Currently waiting on this tick group but we should be able to allow these tasks to run over the whole frame if we can implement some synchronization so they don't overrun the EOF updates.
		ProcessTickList<true>(DeltaTime, TickType, TickGroup, TickLists_Concurrent, MyCompletionGraphEvent);
	}
	else

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Public/FXSystem.h:71

Scope (from outer to inner):

file
namespace    FXConsoleVariables

Source code excerpt:

	extern int32 bFreezeParticleSimulation;
	/** true if we allow async ticks */
	extern int32 bAllowAsyncTick;
	/** Amount of slack to allocate for GPU particles to prevent tile churn as percentage of total particles. */
	extern float ParticleSlackGPU;
	/** Maximum tile preallocation for GPU particles. */
	extern int32 MaxParticleTilePreAllocation;
	/** Maximum number of CPU particles to allow per-emitter. */
	extern int32 MaxCPUParticlesPerEmitter;