Collision is very expensive when spawning instanced Meshes in PCG

Hi,

We noticed when trying to optimize our PCG graphs, that spawning instanced meshes with Collisions is way more expensive than spawning meshes without collision.

Most of the time we do not really need collisions on our meshes that are very far away from the player.

We still want to be able to see them tho.

Is there any built in optimization (something like collision culling?) that we could use here?

Is there any system that we should use. I can see that other games that rely heavily on PCG (eg. the new witcher 4 demo) will have similar issues when spawning meshes far away.

Thanks in advance

[Attachment Removed]

Steps to Reproduce
Spawning meshes that use collision is significantly more expensive than spawning meshes without collision.

[Attachment Removed]

Hi Niklas,

It’s currently a “well-known” problem that spawning physics is expensive, regardless of whether you do it yourself (in c++ or in BP) or in PCG, as this depends solely on the physics system.

However, there are definitely work arounds:

  • if you can, turn off collisions on those objects if they are never going to be collidable (in the Static Mesh Spawner node, use the No Collision for the Collision preset, which is actually the default).
  • you can generate ‘twice’ the same content, once without collision (longer range), and one time *just* with collision (short range), which you can do from PCG - we’ve done that (not turned on by default, but it’s there in the data) in the Cassini Sample demo asteroid field.

Of course, these come with caveats, namely that there’s not going to be physics at some ranges, which might or might not be fine for you.

Alternatively, if you can spawn your content offline (in the editor) then it might be useful to look at FastGeo (experimental plugin in 5.7) that allows async mesh & collision loading, which should help on performance as well.

Hope this helps, but let me know if you have more questions.

Cheers,

Julien

[Attachment Removed]

Hi,

Thanks for the answer.

FastGeo sounds pretty good actually. But we need to spawn everything at runtime since we have a procedural world.

Any chance we could make that work at runtime?

[Attachment Removed]

I checked with the team doing the FastGeo dev, and having FastGeo at runtime is currently a thing in the future that might or might not happen.

But to give you more context on the Witcher 4 demo, I would tell you to first watch the presentation from UnrealFest Stockholm 2025 (https://www.youtube.com/watch?v=icIFFlOyob4) - it dives a bit into what was done to support a kind of mixed approach (offline generated + runtime generated).

In a fully runtime solution, I think having a two-ring solution with visuals at a farther distance + collision just ‘relatively’ up close is going to be the simplest/most proven approach, assuming you don’t need physics at very large distances.

Hope this helps,

Julien

[Attachment Removed]

Thanks, that helps a lot already.

I have 2 other questions regarding this.

We tried using the pcg.FramTime command to limit graphs from consuming too much time on the game thread.

We reduced this to 2ms but for some reason the PCGGenericElement:Execute can still take a huge amount of time. We even found one instance that almost took a second on the gamethread

[Image Removed]Are we understanding this FrameTime limit wrong or why does this part ignore it?

The other issue is more related to mesh spawning. One graph is spawning lots of instanced meshes in a short amount of time.

When this happens “waitForVisibilityTasks” is exploding on the renderthread. I was told this is related to occlusion.

[Image Removed]What can we do in pcg to improve this or make it follow a specific budget we want to set?

Thanks in advance

[Attachment Removed]

Hi again Niklas,

So the first thing to understand is that the frame time really is a budget in the sense that we will stop further processing that given frame as soon as we go over it.

It will then depend very strongly on what’s being run - if the task being run is not time-sliced (e.g. executable over multiple calls/frames) then it will do that whole task, then stop for that given frame.

In this specific case “Generic Element” is unfortunately a - you guessed it - generic call we do for a lot of things, that can be to trigger callbacks, UI updates, etc. It’s going to be hard to know what’s happening here unless you break into it unfortunately. I’ll talk to the team so we add a bit more information in these so it makes it easier to understand what’s happening, but this won’t change until 5.8.

Finally, the visibility update - it’s possible it’s related to occlusion, but it’s ultimately a symptom of you adding meshes to the scene. In this case, there are a few ways that could be changed:

1) spawn your ISMs in such a way that you do more of them, but with less instances, preferably in a smart way (with respect to bounds, which will help culling too).

There are few ways you can do this in PCG (intersect your points with other boxes, or partitioning, etc.), but nothing super trivial for that specific case.

2) it would be possible to push “parts” of the instances to the ISM on a frame-by-frame basis; however, I remember we tried this and it was not behaving super well (because the total sum was a lot bigger than doing it in a single pass).

Ultimately, if this is a problem, I would suggest you look into hierarchical generation - you could do your point logic at larger scales, but spawn on smaller grid sizes (+ you’d cull data per cell).

For example, we’ve done something like this in the Cassini Sample demo, where we’d generate a lot of things up-front, but spawn around the camera on a significantly smaller grid.

Hope this helps.

Cheers,

Julien

[Attachment Removed]

Hi,

Thanks a lot. That helped understand things better.

We will try splitting up our graphs to do less work every frame.

I guess combining meshes will also help here since it will be less instances beeing added at the same time.

Well let you know if we have any further questions :slight_smile:

Greetings,

Niklas

[Attachment Removed]