Gathering Heightfield geometry on a task thread

In our Mercuna Navigation middleware, we can build or rebuild the navigation graph at runtime to support dynamically changing game worlds. We aim to do as much of the work off the game thread as possible to avoid impacting frame rate. We’ve recently found a crash during collection of the geometry slices from a Chaos Landscape Heightfield.

The `INavRelevantInterface` comments that `PrepareGeometryExportSync` must be called on the Game thread, and that `GatherGeometrySlice` can be called “on demand”, which we thought meant it could be called on a worker thread after `PrepareGeometryExportSync` had been called on the game thread. However, it seems this doesn’t work as in this case the cached heightfield data is cleared by level streaming.

Could you clarify whether this is intended to be possible? Is there anything we could do to lock the cached heightfield between the prepare call and the end of the gather call, to prevent it potentially being cleared until we have finished using it?

Steps to Reproduce
The issue is reproduced by moving the camera long distances in a World Partition map causing a large amount of level streaming work, while simultaneously trying to rebuild a part of the Mercuna nav grid.

We could potentially work out a minimal repro case if that would be helpful, but I think this is more a question about whether the API supports, or is intended to support, what we are trying to do - see below.

Was this working as you expected in previous engine versions? I am curious if a change was introduced that is causing this or if it is a wrinkle we had not encountered yet. Is it routinely stemming from the landscape or do other Actors also give this crash?

Hi James, our implementation has been like this for several years and we hadn’t seen the problem before. But I think that is just because we hadn’t previously found a test case that provoked loading part of the landscape at the exact same time that a nav rebuild was occurring - so I’m not sure whether this is related a recent engine change (or maybe a difference between PhysX and Chaos or another UE4 to UE5 change)

However, thinking about the API further, I’m not sure it would be possible to make this work in the case that the heightfield changes (including loading/unloading due to streaming) with the current API calls. There would need to be some kind of instancing of the data, or a locking mechanism, to make that work.

I don’t think this crash is relevant to other actor classes as only landscapes support sliced geometry collection, as far as I’m aware. Mercuna only attempts to collect geometry on worker threads for components that support sliced collection - for all other actors we collect the mesh on the game thread.

I suspect the fix will have to be moving Mercuna’s collection of this geometry to the game thread, but I just wanted to check whether the intention was to allow multithreaded access here or not.

So our thought is that it should work. If you could supply us with a small repro, we would gladly look into it. We are curious how the worker thread is processing the landscape component, but it seems the landscape component is in the midst of its own registration on the game thread. There may be a problematic code path that allows that to happen that we just have not discovered yet.

Thank you for the repro project! I can confirm it is still happening in our Main branch, and I created a bug report for us to investigate what is happening. Here is a link to the report on our public issue tracker: https://issues.unrealengine.com/issue/UE-300319. It can take a couple days to be approved and mirrored, but it will stay in sync once it has done its initial mirroring.

-James

That’s great to hear that you expect this should work. We will look into putting together a repro project. Thanks!

This repro case demonstrates the problem.

`UMyBlueprintFunctionLibrary::ReproduceIssue` does `PrepareGeometryExportSync` and then kicks off a task that calls `GatherGeometrySlice`. This will crash in `FReproGeometryExportClass::ExportChaosHeightFieldSlice` if called while the camera is being jumped around to provoke streaming.

The level blueprint will trigger the issue if you play the ReproMap in editor.