LineTraceSingleByChannel slow in WorldPartitionBuilderCommandlet

I’m writing a world partition builder that goes through the world and in our main world where the physics is complex, each call to LineTraceSingleByChannel takes around 5ms compared to normal runtime or editor use where it’s less than 0.1ms.

I enable trace collision in the world initialization values.

UWorld::InitializationValues UProcessDataWorldPartitionBuilder::GetWorldInitializationValues() const
{
	UWorld::InitializationValues IVS = Super::GetWorldInitializationValues();
	IVS.EnableTraceCollision( true );
	return IVS;
}

It’s slow because it takes a different route in the AABBTree, on line 2955 “if (DirtyElements.Num() > 0)” is true which is different from how it is normally (when it’s fast).

Somehow some physics acceleration structure isn’t built, so my calls are very slow. I’m not sure how to ensure they are built.

in “RunInternal” I’ve tried adding

FStaticMeshCompilingManager::Get().FinishAllCompilation();
FWorldPartitionHelpers::FakeEngineTick( _world );

World->GetPhysicsScene()->Flush();

to ensure that all the meshes are compiled and the physics structure is ready, but it doesn’t work. There is something I’m missing, but I don’t know what.

Thanks in advance.

slow_trace.png(330 KB)

Steps to Reproduce

  1. Inherit from UWorldPartitionBuilder
  2. Override RunInternal and perform a lot of traces in a world with very complex physics
  3. Notice that each individual trace is very slow

Hi Joel,

From the description, you’ve got a good amount of the way there yourself. The DirtyElements array holds objects which haven’t made it into our spatial partitioning structure yet, and need to be searched linearly - as you called out, this is almost certainly the reason for the slowdown.

I’m not 100% certain what is causing this - but again from context it sounds like the physics engine is getting overloaded or there is a variable there which is limiting the time chaos can spend updating the spatial acceleration structure (which is how the dirty elements get removed from the list and find their way into the AABB tree). This can be a common situation when there is a lot of geometry loaded at once, and the physics has a lot of work which needs to be done in order to build a huge AABB tree.

There are a lot of CVars in AABBTree.cpp which can affect this process, so I’d check there and see if it is different from the default settings. I’d also try and add a delay between loading and starting raycasting. Finally you can add some debug output into Reset and GenerateTree (in AABBTree.h) which would allow you to see if there is a reason why the dirty list isn’t getting cleared as often as expected.

All the best

Geoff Stacey

Developer Relations

EPIC Games

Great stuff Joel, and thank you for updating this - I’m sure anyone reading this will be sending you good vibes :slight_smile:

Best

Geoff

I removed my initial reply with my solution due the commandlet not working as expected. Now it’s confirmed to be working, so I’m returning with what I did in the end in case it helps someone else.

bool UMyWorldPartitionBuilder::PreRun( UWorld* World, FPackageSourceControlHelper& PackageHelper )
{
...
	UKismetSystemLibrary::ExecuteConsoleCommand( World, "p.aabbtree.DirtyElementMaxCellCapacity 100000", nullptr );
...
}
 
 
bool UMyWorldPartitionBuilder::RunInternal( UWorld* World, const FCellInfo& InCellInfo, FPackageSourceControlHelper& PackageHelper )
{
...
	// Iterating on the minimum permutation of these calls is slow for me, so I haven't done it yet. This works for us...
	FStaticMeshCompilingManager::Get().FinishAllCompilation();
	for ( int i = 0; i < 10; ++i )
	{
		FWorldPartitionHelpers::FakeEngineTick( World );
	}
	World->GetPhysicsScene()->Flush();
	World->GetPhysicsScene()->Flush();
	World->GetPhysicsScene()->Flush();
	for ( int i = 0; i < 10; ++i )
	{
		FWorldPartitionHelpers::FakeEngineTick( World );
	}
...
	// Perform line traces here
}