Physics - Async Ticking Crash

Physics - Async Ticking Crash

(Sorry, I’m not entirely sure if this is a bug report, question, discussion etc.)

Looks like my game is crashing when an actor that is async ticking is streamed out by the world partition. I did not found anything official about this, but there was a discussion about it:

I think this is still present (at least in my 5.4 game). I also realized that Unreal added a check for this in 5.5 in PhysScene_Chaos.cpp

for(AActor* Actor : AsyncPhysicsTickActors)
			{
				check(Actor)

				FScopeCycleCounterUObject ActorScope(Actor);
				Actor->AsyncPhysicsTickActor(DeltaTime, SimTime);
			}

First of all, this is a raw pointer, so I’m not sure what’s the point of the check since its not null anyways - its quite unreliable.

Also, according to the discussions above Id have to implement the unregister per actor in my custom class. Why don’t we have a simple UnregisterAsyncPhysicsTickActor call on EndPlay in Actor.cpp (just like we have a RegisterAsyncPhysicsTickActor in BeginPlay, called RegisterAllActorTickFunctions). Wouldn’t it be safe to say that if the actor is bAsyncPhysicsTickEnabled, then it should try to unregister itself on EndPlay?

We do unregister it when you call Destroy() on the actor, but looks like the world streaming does not call that, it only calls RouteEndPlay, and seemingly that does not care about async physic ticking.

I don’t get it how isn’t it a prominent issue, or why cant I find anything about it from Epic side? Am I missing something?

TLDR shouldnt we have an EndPlay in Actor.cpp similar to this

void AActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	if (ActorHasBegunPlay == EActorBeginPlayState::HasBegunPlay)
	{
		TRACE_OBJECT_LIFETIME_END(this);

		ActorHasBegunPlay = EActorBeginPlayState::HasNotBegunPlay;

#if UE_WITH_IRIS
		// This must be called otherwise the ReplicationSystem will keep a reference to the actor forever.
		EndReplication(EndPlayReason);
#endif

		// Dispatch the blueprint events
		ReceiveEndPlay(EndPlayReason);
		OnEndPlay.Broadcast(this, EndPlayReason);

		TInlineComponentArray<UActorComponent*> Components;
		GetComponents(Components);

		for (UActorComponent* Component : Components)
		{
			if (Component->HasBegunPlay())
			{
				Component->EndPlay(EndPlayReason);
				ensureMsgf(Component->HasBegunPlay() == false, TEXT("EndPlay on %s failed. Make sure to call Super::EndPlay() in the override function in class %s."), *Component->GetName(), *Component->GetClass()->GetName());
			}
		}
 
               /* Updated part - unregistering async ticking*/
		if (FPhysScene_Chaos* Scene = static_cast<FPhysScene_Chaos*>(GetWorld()->GetPhysicsScene()))
		{
			if (bAsyncPhysicsTickEnabled)
			{
				Scene->UnregisterAsyncPhysicsTickActor(this);
			}
		}
	}
}

I recently tried registering on BeginPlay and unregister on EndPlay, the crashes on unloading are gone. But the game still crahes occasionally. Even when my game is doing not really a lot and i am idling at my spawn position.

Also when I set time dilation to 2, 4 or 8 the crash happens much earlier, the higher i set the time dilation.

For that reason I removed the async ticked objects in my world (simple things like barrels). Now everything is fine.

1 Like

@sergio.gardeazabal Sorry for the random tag, but your response was super useful before, any input on this? :expressionless: Thanks in advance!

Hi,

Looking at the code in the latest version, the Async Tick (alongside other tick functions) should be unregistered when the actor is either destroyed or when a level is streamed out (in which case components are unregistered incrementally). See ULevel::IncrementalUnregisterComponents.

If RegisterAllActorTickFunctions is not being called as part of that, then something is not working as expected.

Can you share the call stack of the crash? I can check with the team to see if this is a known issue or if it is already fixed.

If the Actor or Component ptr is not null, but points to invalid memory. It means the actor was garbage collected without being removed from the async callback list. So yes, in that case the check you saw will likely not help to catch that issue.

1 Like

Do we even have to tick async physics on such objects (barrels, barriers…) that lay around in the world, waiting to be interacted with a pawn that is using the async physics tick to add forces to its body?

1 Like

It depends on your implementation and what you are trying to achieve.

Just to add forces in a specific situation (like colliding with a pawn), you should be able to apply the force you want to the component/body you want using the normal API in the game thread.

The async physics system will take care of applying it at the correct time in the physics thread.

The Async Physics tick allows you to do specific work each time the physics engine advances one step (which is not necessarily each time your actor/component ticks, because with async physics the physics engine tick and the game thread tick are decoupled).

1 Like

just simple actors that have the simulate physics/enable gravity flags enabled, nothing else. the only thing that is using async physics is the player pawn (car).

1 Like

I just confirmed it and its still there in 5.4.2 in a fresh demo project. (I tested it in 5.5, that also crashed, but with a different callstack, not sure if its the same).

5.4 crash callstack (this is the actual crash im facing with my project):

Unhandled Exception: EXCEPTION_ACCESS_VIOLATION 0x000000c4000000a6

UnrealEditor_Engine!FAsyncPhysicsTickCallback::OnPreSimulate_Internal() [Engine\Source\Runtime\Engine\Private\PhysicsEngine\Experimental\PhysScene_Chaos.cpp:212]
UnrealEditor_Chaos!Chaos::FPhysicsSolverBase::ApplyCallbacks_Internal() [Engine\Source\Runtime\Experimental\Chaos\Public\Chaos\Framework\PhysicsSolverBase.h:577]
UnrealEditor_Chaos!Chaos::FPBDRigidsSolver::ApplyCallbacks_Internal() [Engine\Source\Runtime\Experimental\Chaos\Private\PBDRigidsSolver.cpp:784]
UnrealEditor_Chaos!Chaos::FPhysicsSolverFrozenGTPreSimCallbacks::GTPreSimCallbacks() [Engine\Source\Runtime\Experimental\Chaos\Private\Chaos\Framework\PhysicsSolverBase.cpp:134]
UnrealEditor_Chaos!TGraphTask<Chaos::FPhysicsSolverFrozenGTPreSimCallbacks>::ExecuteTask() [Engine\Source\Runtime\Core\Public\Async\TaskGraphInterfaces.h:1235]
UnrealEditor_Core!FNamedTaskThread::ProcessTasksNamedThread() [Engine\Source\Runtime\Core\Private\Async\TaskGraph.cpp:760]
UnrealEditor_Core!FNamedTaskThread::ProcessTasksUntilQuit() [Engine\Source\Runtime\Core\Private\Async\TaskGraph.cpp:651]
UnrealEditor_Core!FTaskGraphCompatibilityImplementation::WaitUntilTasksComplete() [Engine\Source\Runtime\Core\Private\Async\TaskGraph.cpp:2122]
UnrealEditor_Engine!FTickTaskSequencer::ReleaseTickGroup() [Engine\Source\Runtime\Engine\Private\TickTaskManager.cpp:556]
UnrealEditor_Engine!FTickTaskManager::RunTickGroup() [Engine\Source\Runtime\Engine\Private\TickTaskManager.cpp:1583]
UnrealEditor_Engine!UWorld::RunTickGroup() [Engine\Source\Runtime\Engine\Private\LevelTick.cpp:772]
UnrealEditor_Engine!UWorld::Tick() [Engine\Source\Runtime\Engine\Private\LevelTick.cpp:1521]
UnrealEditor_UnrealEd!UEditorEngine::Tick() [Engine\Source\Editor\UnrealEd\Private\EditorEngine.cpp:2015]
UnrealEditor_UnrealEd!UUnrealEdEngine::Tick() [Engine\Source\Editor\UnrealEd\Private\UnrealEdEngine.cpp:550]
UnrealEditor!FEngineLoop::Tick() [Engine\Source\Runtime\Launch\Private\LaunchEngineLoop.cpp:5921]
UnrealEditor!GuardedMain() [Engine\Source\Runtime\Launch\Private\Launch.cpp:180]
UnrealEditor!GuardedMainWrapper() [Engine\Source\Runtime\Launch\Private\Windows\LaunchWindows.cpp:118]
UnrealEditor!LaunchWindowsStartup() [Engine\Source\Runtime\Launch\Private\Windows\LaunchWindows.cpp:258]
UnrealEditor!WinMain() [Engine\Source\Runtime\Launch\Private\Windows\LaunchWindows.cpp:298]
UnrealEditor!__scrt_common_main_seh() [src\vctools\crt\vcstartup\src\startup\exe_common.inl:288]
kernel32
ntdll

5.5 crash callstack (im not sure what happened with the lines here):

Assertion failed: Index >= 0 [...\Engine\Source\Runtime\CoreUObject\Public\UObject\UObjectArray.h] [Line: 943] 

UnrealEditor_Engine
UnrealEditor_Engine
UnrealEditor_Engine
UnrealEditor_Chaos
UnrealEditor_Chaos
UnrealEditor_Chaos
UnrealEditor_Chaos
UnrealEditor_Core
UnrealEditor_Core
UnrealEditor_Core
UnrealEditor_Core
UnrealEditor_Engine
UnrealEditor_Engine
UnrealEditor_Engine
UnrealEditor_Engine
UnrealEditor_UnrealEd
UnrealEditor_UnrealEd
UnrealEditor
UnrealEditor
UnrealEditor
UnrealEditor
UnrealEditor
UnrealEditor
kernel32
ntdll

To repro:
1 Create new project
2 Make sure world streaming is enabled
3 Create new blueprint actor
4 Add async physics tick with a print “X” node
5 Run console command gc.CollectGarbageEveryFrame 1
6 Add x actor to the level
7 Run Simulate while looking at the actor (print X should appear on screen)
8 Move the camera out so that it unloads → Crash

The bug does not happen if async physics tick or world streaming are disabled.

About actors / components ticking:
I might have a misconception here. There is no such thing as async ticking actor? If an actor is asyn ticking, does that mean that one of its component is async ticking? What happens then if I click the Enable async ticking on an actor?

And yes, it might be GC because debugging it it looks like its a valid address (I see the array element being Invalid Label - Invalid etc.), and can 10/10 repro with gc.CollectGarbageEveryFrame 1.

Unfortunatelly I did not actually find where should the RegisterAllActorTickFunctions call the UnregisterAsyncPhysicsTickActor(this);, that’s why the confusion

They yes, it is not needed.

You can have async physics enabled (that is project wide setting), but have the async physics tick disabled in your objects.

The async physics tick is only needed if you need to do work at the physics step level (make sense in a vehicle simulation).

Even then, the async physics tick has a non-trivial per impact, so if you don’t need blueprint support for the work you are doing in these, it would be better to implement your own sim callback object

See TSimCallbackObject.

Sadly we don’t have a tutorial on these yet, but depending on what version of UE you are, there are a few code examples in engine, as a few systems use these sim callback objects.

1 Like

Thank you for the report with repro steps.

I logged a ticked internally and we will investigate the issue.

About actors / components ticking:
I might have a misconception here. There is no such thing as async ticking actor? If an actor is asyn ticking, does that mean that one of its component is async ticking? What happens then if I click the Enable async ticking on an actor?

Actors and component have their own AsyncPhysicsTick method, and they can be registered/unregistered independently into the async tick callback system. But both are executed by the same method in the same scope.

As I mentioned in an earlier response, if you don’t need to do work on a physics step level, we don’t recommend using the async physics tick (Physics will still be async and work as intended, as long you have async physics enabled in your project settings).

If you do need that kind of work, but don’t need to do it in blueprints, then the recommended way is to implement your own sync callback with TSimCallbackObject . Async physics tick has a non-trivial perf impact.

Unfortunatelly I did not actually find where should the RegisterAllActorTickFunctions call the UnregisterAsyncPhysicsTickActor(this);, that’s why the confusion

You should not have to call them manually, I think there is a bug here where a code path used during level streaming is not performing the expected unregistered call. We will need to investigate it further

I am using the async physics tick plugin, also not using any blueprints at all except for data holders :slight_smile: