Is there a way to call UProceduralMeshComponent::CreateMeshSection on a separate thread?

I’m generating terrain using the procedural mesh component at runtime. There is a noticeable stutter when generating new chunks of terrain that I would like to resolve. I’ve tried calling UProceduralMeshComponent::CreateMeshSection in an AsyncTask on FNamedThreads::Type::AnyThread but it crashes. Is the UProceduralMeshComponent thread safe? Is there any way to have the mesh generated asynchronously to avoid a stutter?

3 Likes

I was able to create the procedural mesh component on a separate thread and then call CreateMeshSection on that thread before registering and attaching the component to the actor on the game thread. This seems to work, although, occasionally I get a crash in GC if I stop the game before the mesh has been completely generated. I needed to remove the Async flags of the procedural mesh component and its ProcMeshBody setup for garbage collection.

AsyncTask(ENamedThreads::AnyThread, [this]()
	{
		bGenerating = true;

		Mesh = NewObject<UProceduralMeshComponent>(this, UProceduralMeshComponent::StaticClass());
		Mesh->ClearInternalFlags(EInternalObjectFlags::Async); /* Called to allow garbage collection visit: https://georgy.dev/posts/avoid-gc-async/ for details */
		
		CreateVertices();
		CreateTriangles();

		TArray<FColor> VertexColors;
		bool bCreateCollision = true;

		UKismetProceduralMeshLibrary::CalculateTangentsForMesh(Vertices, Triangles, RenderUVs, Normals, Tangents);

		Mesh->CreateMeshSection(0, Vertices, RenderTriangles, Normals, RenderUVs, VertexColors, Tangents, bCreateCollision);
		Mesh->ProcMeshBodySetup.Get()->ClearInternalFlags(EInternalObjectFlags::Async);

		AsyncTask(ENamedThreads::GameThread, [this]()
			{
				if (Mesh.IsValid())
				{
					Mesh->RegisterComponent();
					Mesh->AttachToComponent(RootComponent, FAttachmentTransformRules::SnapToTargetIncludingScale);
					Mesh->SetMaterial(0, Material);
				}
			});

		bGenerating = false;
	});
3 Likes