FX.FreezeParticleSimulation

FX.FreezeParticleSimulation

#Overview

name: FX.FreezeParticleSimulation

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

It is referenced in 11 C++ source files.

#Summary

#Usage in the C++ source code

The purpose of FX.FreezeParticleSimulation is to control the freezing of particle simulation in Unreal Engine’s visual effects system. This setting variable is used to pause or resume the simulation of particles, which is particularly useful for debugging, performance analysis, or creating specific visual effects.

This setting variable is primarily used in the Engine’s particle system and visual effects modules. Based on the callsites, it’s utilized in the following subsystems:

  1. FXSystem (Particles/FXSystem.cpp)
  2. ParticleComponents (Particles/ParticleComponents.cpp)
  3. ParticleGpuSimulation (Particles/ParticleGpuSimulation.cpp)
  4. ParticleSystemManager (Particles/ParticleSystemManager.cpp)

The value of this variable is set through the console variable system. It can be modified using console commands or through the level editor actions (LevelEditorActions.cpp).

FX.FreezeParticleSimulation interacts with several other variables and systems:

  1. It’s associated with the bFreezeParticleSimulation variable, which shares the same value.
  2. It’s often used in conjunction with FX.FreezeGPUSimulation and FX.AllowAsyncTick.
  3. It affects both CPU and GPU particle simulations.

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

  1. It’s marked as a cheat variable (ECVF_Cheat), indicating it should not be used in production builds.
  2. Freezing particle simulation can affect gameplay and visual fidelity, so it should be used carefully in development and debugging scenarios.
  3. It impacts both CPU and GPU particle simulations, potentially affecting performance metrics.

Best practices when using this variable include:

  1. Use it primarily for debugging and performance analysis.
  2. Remember to unfreeze the simulation when done to avoid unexpected behavior.
  3. Consider the impact on both CPU and GPU simulations when freezing particles.
  4. Use in conjunction with other debugging tools to get a comprehensive view of particle system performance.

Regarding the associated variable bFreezeParticleSimulation:

The purpose of bFreezeParticleSimulation is to act as the internal representation of the FX.FreezeParticleSimulation console variable. It’s used directly in the engine code to control particle simulation freezing.

This variable is used in the same subsystems as FX.FreezeParticleSimulation, primarily in the particle and visual effects modules of the engine.

The value of bFreezeParticleSimulation is set through the console variable system, mirroring the value of FX.FreezeParticleSimulation.

It interacts closely with FX.FreezeParticleSimulation and is often checked in conjunction with other particle system variables like bFreezeGPUSimulation and bAllowAsyncTick.

Developers should be aware that this is an internal variable and should generally interact with it through the FX.FreezeParticleSimulation console variable rather than directly.

Best practices for bFreezeParticleSimulation are similar to those for FX.FreezeParticleSimulation, focusing on its use in debugging and development scenarios rather than in production code.

#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:158

Scope (from outer to inner):

file
namespace    FXConsoleVariables

Source code excerpt:

		);
	FAutoConsoleVariableRef CVarFreezeParticleSimulation(
		TEXT("FX.FreezeParticleSimulation"),
		bFreezeParticleSimulation,
		TEXT("Freeze particle simulation."),
		ECVF_Cheat
		);
	FAutoConsoleVariableRef CVarAllowAsyncTick(
		TEXT("FX.AllowAsyncTick"),

#Loc: <Workspace>/Engine/Source/Editor/LevelEditor/Private/LevelEditorActions.cpp:2805

Scope (from outer to inner):

file
function     void FLevelEditorActionCallbacks::OnToggleFreezeParticleSimulation

Source code excerpt:

{
	IConsoleManager& ConsoleManager = IConsoleManager::Get();
	IConsoleVariable* CVar = ConsoleManager.FindConsoleVariable(TEXT("FX.FreezeParticleSimulation"));
	if (CVar)
	{
		CVar->Set(CVar->GetInt() == 0 ? 1 : 0, ECVF_SetByConsole);
	}
}

#Loc: <Workspace>/Engine/Source/Editor/LevelEditor/Private/LevelEditorActions.cpp:2815

Scope (from outer to inner):

file
function     bool FLevelEditorActionCallbacks::OnIsParticleSimulationFrozen

Source code excerpt:

{
	IConsoleManager& ConsoleManager = IConsoleManager::Get();
	static const auto* CVar = ConsoleManager.FindConsoleVariable(TEXT("FX.FreezeParticleSimulation"));
	if (CVar)
	{
		return CVar->GetInt() != 0;
	}
	return false;
}

#Associated Variable and Callsites

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

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

Scope (from outer to inner):

file
namespace    FXConsoleVariables

Source code excerpt:

	int32 bAllowCulling = true;
	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;

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

Scope (from outer to inner):

file
namespace    FXConsoleVariables

Source code excerpt:

	FAutoConsoleVariableRef CVarFreezeParticleSimulation(
		TEXT("FX.FreezeParticleSimulation"),
		bFreezeParticleSimulation,
		TEXT("Freeze particle simulation."),
		ECVF_Cheat
		);
	FAutoConsoleVariableRef CVarAllowAsyncTick(
		TEXT("FX.AllowAsyncTick"),
		bAllowAsyncTick,

#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)
		{
			if (!FXConsoleVariables::bFreezeParticleSimulation)
			{
				ComputeTickComponent_Concurrent();
			}
			FinalizeTickComponent();
		}
		else

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

Scope (from outer to inner):

file
function     void UParticleSystemComponent::FinalizeTickComponent

Source code excerpt:

	bNeedsFinalize = false;

	if (FXConsoleVariables::bFreezeParticleSimulation == false)
	{
		int32 EmitterIndex;
		// Now, process any events that have occurred.
		for (EmitterIndex = 0; EmitterIndex < EmitterInstances.Num(); EmitterIndex++)
		{
			FParticleEmitterInstance* Instance = EmitterInstances[EmitterIndex];

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Particles/ParticleGpuSimulation.cpp:3385

Scope (from outer to inner):

file
class        class FGPUSpriteParticleEmitterInstance : public FParticleEmitterInstance
function     virtual FDynamicEmitterDataBase* GetDynamicData

Source code excerpt:

		const bool bSimulateGPUParticles = 
			FXConsoleVariables::bFreezeGPUSimulation == false &&
			FXConsoleVariables::bFreezeParticleSimulation == false &&
			RHISupportsGPUParticles();

		if (bSimulateGPUParticles)
		{
			float& DeltaSecondsInFix = DynamicData->PerFrameSimulationParameters.DeltaSecondsInFix;
			int32& NumIterationsInFix = DynamicData->PerFrameSimulationParameters.NumIterationsInFix;

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Particles/ParticleGpuSimulation.cpp:3603

Scope (from outer to inner):

file
class        class FGPUSpriteParticleEmitterInstance : public FParticleEmitterInstance
function     virtual void Tick

Source code excerpt:


		if (FXConsoleVariables::bFreezeGPUSimulation ||
			FXConsoleVariables::bFreezeParticleSimulation ||
			!RHISupportsGPUParticles())
		{
			return;
		}

		// Grab the current LOD level

#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:69

Scope (from outer to inner):

file
namespace    FXConsoleVariables

Source code excerpt:

	extern int32 bFreezeGPUSimulation;
	/** true if particle simulation is frozen. */
	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;