Possible cook determinism issue for StaticMesh when transformed with FastGeoWorldPartitionRuntimeCellTransformer

Hi,

I was recently investigating an issue related to determinism around fastgeo and static meshes. After some investigation (with some AI support), we found that some properties of static meshes that would be transformed would be different than expectations. Delving into the code, we found a call to StaticMeshComponent->UpdateCollisionFromStaticMesh(). It so happens that if the static mesh is not done compiling, that function is directly a no-op and the update is skipped.

As a solution, we added locally a compilation flush (FAssetCompilingManager::Get().FinishCompilationForObjects(ObjectsToFlush)) for all the actor components in the level being processed by FastGeoWorldPartitionRuntimeCellTransformer as a final step before starting the transform. Adding it to pre-transform would add ordering issues that is not desirable. We hoped that by including all components with a IInterface_AsyncCompilation and IsCompiling()==true, we’d prevent future problems for other compilable resources.

Have a nice day,

Maxime Thibeault.

[Attachment Removed]

Steps to Reproduce

  1. Cook using a world partition and static meshes, including generated static meshes from blueprints. (Should satisfy the following query: if (bUseDefaultCollision && SupportsDefaultCollision())
  2. Check the cook for determinism issues.
    [Attachment Removed]

Hi,

Can you clarify why adding the flush pre-transform would introduce ordering issues? It appears that UWorldPartition::ApplyRuntimeCellsTransformerStack would be the proper place to flush just before the first call to ApplyTransformPhase.

Martin

[Attachment Removed]

Hi,

There are no explicit rules regarding RCTs but the intention is to work on pre-existing instances and their referenced assets. The injection of compilable assets (StaticMesh, Materials, Textures…) in the cells is unknown territory and would require extra caution\validation. In the normal case, I feel that waiting for the compilation of components part of the current cell should be enough. It would be up to new\custom RCTs that inject assets to make sure they are in a usable state. In UWorldPartition::ApplyRuntimeCellsTransformerStack:

void UWorldPartition::ApplyRuntimeCellsTransformerStack(ULevel* InLevel)
{
...
 
		//Making sure that Primitves are done compiling to avoid cooking determinism problems when applying Runtime Cell Transformers. 
		TArray<UObject*> ObjectsToFlush;
 
		//Guestimating to save on reallocation costs. 
		ObjectsToFlush.Reserve(InLevel->Actors.Num());
 
		for (AActor* Actor : InLevel->Actors)
		{
			if (IsValid(Actor))
			{
				Actor->ForEachComponent<UPrimitiveComponent>(/*bIncludeFromChildActors*/ true,
					[&ObjectsToFlush](UPrimitiveComponent* Component)
					{
						if (Component->IsCompiling())
						{
							ObjectsToFlush.Emplace(Component);
						}
					});
			}
		}
 
		FAssetCompilingManager::Get().FinishCompilationForObjects(ObjectsToFlush);
 
		{
			TRACE_CPUPROFILER_EVENT_SCOPE(PreTransform);
			ApplyTransformPhase([](const FRuntimeCellTransformerInstance& TransformerInstance, ULevel* Level) { TransformerInstance.PreTransform(Level); });
		}
 
...

I agree that UpdateCollisionFromStaticMesh should not lie. I checked the history of that code and it dates back to 2020 when we introduced the async build for assets in the editor. My guess is that this was deemed minor at the editor level but the case for RCTs didn’t exists at the time. The owner of that code is currently out of office but I intend to validate when he is back.

Martin

[Attachment Removed]

FYI, we are circling back to a local wait as you mentioned in your original post. We don’t want to flush the async compilation unless it’s absolutely necessary.

[Attachment Removed]

Hi Martin,

There are no rules about what can and cannot be done during the main transform phase which means that if some previous transformer are creating static meshes components, those components’ meshes could be in an unfinished state even at transform time. For the check to be put in the pre-transform, we’d need to ensure that any and all work on “compilable” assets is done before that pre-transform and that no new static mesh components or other “compilable” assets are generated which may not be the case. It is particularly affecting FastGeoWorldPartitionRuntimeCellTransformer considering that it is usually one of the last transformer to be ran.

In reality, it is probably a more generic problem where “UpdateCollisionFromStaticMesh()” is lying about its completion, returning early if it is still compiling without doing its work. I assume that forcing end of compilation during UpdateCollisionFromStaticMesh would also fix that particular problem since it is already getting called with expectation that it does work during the Transform (in UFastGeoWorldPartitionRuntimeCellTransformer, from Transform->GatherTransformableActors->CanTransformActor)

Maxime.

[Attachment Removed]