Nanite landscape switches to non-nanite

We are using a pretty standard landscape, the only special treatment is that it’s scaled down to 0.25 scale, and that we use a few landscape splines (even though we bake them down as we go, and the affected areas do not have any on them). Since nanite was introduced for landscapes, we have used it, but we also have the issue that chunks of the landscape will stop being nanite, and they need to be constantly rebuilt to snap back into nanite mode. The material is a regular terrain material, opaque, with a masked hole material as recommended.

Hi there,

The Nanite representation of the landscape mesh needs to be rebuilt every time the landscape is modified. Since building the nanite representation of a mesh is quite slow, the process is deferred until either a manual build or on saving the landscape. If you’re getting the landscape snapping back to non-nanite mode without having made any manual modifications, maybe you have something in your level that is actively modifying the landscape in editor. Perhaps you have some construction script that is running and moving a landscape spline for instance.

Regards,

Lance Chaney

Hi,

we are having similar issue where nanite enabled landscape falls back to non-nanite in standalone and builds, but works fine in PIE.

At first I thought that landmass brush blueprints causes this, as it was consistently breaking working landscapes. But while trying to get 100% repro steps, even clear landscape on an empty level stopped working with nanite, logs were always outputting this:

MapCheck: Warning: LandscapeStreamingProxy_1_1_0 Landscape Nanite is enabled but the saved mesh data is out of date. Save Modified LandscapesEven though editor always said saving was successful.

LogLandscape: Successful export of raw static mesh for Nanite landscape (16 components) for actor LandscapeStreamingProxy_1_1_0If at some point we could get empty landscape to work with nanite in standalone or build, those steps would always cause affected streaming proxies to fall back to non-nanite:

  1. Enter landscape edit mode, and go to manage tab
  2. Select blueprint mode
  3. Select landmass brush (we tried with inhouse custom made brushes and “custombrush_Landmass” available from plugin
  4. At this point affected streaming proxies will no longer work with nanite landscape in builds and/or standalone. They will however work fine in PIE

This was tested in 5.6.0, we will hopefully be able to test this in 5.6.1 next week.

Any solution to this would be greatly appreciated.

Edit/

Deleting landmass brushes that would force non-nanite fallback would never fix the issue. Even if the same landscape would previously work correctly.

Hi [mention removed]​,

We are have the same problem in 5.6 in our main map. Loaded landscape proxies in the editor use Nanite, then they randomly turn non-nanite and a warning will ask for rebuild. The weird thing is that loading a single proxy is fine, but loading a adjacent proxy might remove nanite from one of them, or both.

I debugged the code and this is what triggers it:

  1. Load a proxy that trigger the loading of splines
  2. ALandscape::RequestSplineLayerUpdate is called, which trigger ALandscape::RegenerateLayersHeightmaps on all proxies
  3. In ALandscape::ResolveLayersTexture, the texture hash will be different on some proxies (this looks random)
  4. ALandscapeProxy::RemoveNaniteComponents is called on those proxies
  5. The proxies turns non-nanite and the warning show up. The actors are not dirtied.

What I noticed is that the difference between the old and new mip data (in ALandscape::ResolveLayersTexture) seems to be on the edges. Rebuilding and saving the landscape doesn’t work, because the hash seems to be dependant on which adjacent proxies are loaded. So reloading the map or loading different sets of proxies will result in different proxies becoming non-nanite.

I integrated this change that looked promissing, but it sadly didn’t fix it: https://github.com/EpicGames/UnrealEngine/commit/6bb85f022768e4ca80afe4b10368603f1358558c

Do you know of other changes that could help?

Thanks for the extra information and debugging. I was able to reproduce this issue on my end with this extra info. My reproduction has a long landscape spline going all over the map. Repeatedly re-opening this map causes the issue to trigger, with the same call sites triggering the issue that you noted. [Image Removed]I also cherry-picked the commit you noted, and found that it does resolve the issue for me. At least with this reproduction case. One gotcha was that I needed to rebuild the nanite landscape once, after the cherry-pick, for the issue to go away permanently. Likely this is because the new hashes don’t match the saved data after the update. I also note that I needed to cherry-pick one other pre-requisite commit before this would compile. The two commits I cherry picked were:

CL 43814550

CL 43856326

Can you double check you have both of these commits and that the issue is not solved by them? If it is not, does it at least fix the issue in some cases (e.g. works in editor, but not in builds, or fixes some invalidation, but not all).

Regards,

Lance Chaney

Ah, I see, you are right that this still happens if I only load the region where the issue originally occurred, or the one across the border from it.

I’ve verified that this still happens up-stream in the ue5-main branch, so I’ve made a bug report for this. Once the bug report has been triaged, it should become publicly available and be visible here for you to vote on. Unfortunately I don’t have a great work-around for this currently, I suppose you could probably fix the regions with issues by moving the spline points slightly until the issue goes away.

I had a brief look at debugging this in PIX, but it’s hard to tell if this is intended behavior or not. Comparing a working vs non-working heightmap update passes, these textures always seems to have this missing row / columns information on the right and bottom edges of the scratch buffer, regardless of whether the hash ends up different or not. The misalignment could possibly introduce some strange determinism issues though, since it seems like each landscape section mighty be slightly overlapping in the merged output.

I will continue to look into this a little further on Tuesday. However, as a bug report has been created, I will probably close this case out if I can’t get anywhere in a reasonable amount of time and leave it to a subject matter expert at Epic to fix.

Regards,

Lance Chaney

Hi,

We do not have anything modifying the landscape via construction script or similar. The only thing that impacts it is splines. Is it possible that splines are the culprit?

Do you see the following warning when the landscape swaps back to non-nanite mode (Note: this warning doesn’t show in landscape mode, but should show in regular selection mode):

[Image Removed]This message means that something has modified your landscape, and causing it to need to be rebuilt.

The landscape splines should only update the landscape when any of the points are moved.

I recommend putting a breakpoint in ALandscape::RequestSplineLayerUpdate (on the line where bSplineLayerUpdateRequested = true;

), and at the top of ULandscapeInfo::MarkObjectDirty to see if your splines are actually modifying the landscape when your nanite data becomes dirty. Note that you may get requests for spline layer updates, but not get calls to MarkObjectDirty (for instance, on map load). This indicates that although a spline update request was made, and possibly performed, the resulting landscape still hashes to the same value, so is not considered dirty.

Let me know if you have any success with this

Regards,

Lance Chaney

Hi Lance,

I tried reproducing this now through the debugger and I see this bug happening without ULandscapeInfo::MarkObjectDirty triggering.

The spline actor triggers RequestSplineLayerUpdate as you say, but nothing more.

It seems like it is probably not the splines causing the landscape dirtying in this case then. Other dirtying sources are unfortunately a bit more indirect and harder to debug. Can you reproduce this issue at all in a separate project? If you could provide a minimal reproduction project we should be able to debug what the cause of the dirtying is.

Do you get the warning indicating that landscape actors with nanite meshes need to be rebuilt? I want to make sure your issue is actually caused by landscape modification, and not some other source.

Regards,

Lance Chaney

When opening the level do get a message about Ladscape Nanite is enabled but the saved mesh data is out of date on a seemingly random amount of landscape cells.

Are different landscape components dirty each time? Or are they the same ones that always seem to dirty themselves?

I think I replicated this issue in standalone game mode. This issue seems to be caused by missing serialized TextureHashGUID in the heightmap textures AssetUserData. The hash calculation for the heightmap data then falls back to a different hash value, causing a mismatch. I may open a bug report for this, however, I was not able to replicate a similar issue in builds

All the checks I can see for un-built nanite data only happen in editor, so I’m surprised you’re getting issues in builds at all. My theory is that the nanite data is probably getting invalidated during the cook process somehow in this case, but have been unable to replicate.

In my testing I also found that any modification at all to a landscape components height map would permanently break that component from then on in standalone game mode, not just landmass. For instance, just sculpting on a landscape component was enough to permanently break it in standalone for me. However, I have not had any issues in actual builds. I tested a development and test build, using `r.Nanite.Visualize Overview` to visualize whether nanite was working or not, and had no issues there.

Would you be able to provide a reproduction project for this issue (with the landscape broken in builds and landmass brush removed)?

Regards,

Lance Chaney

I integrated those 2 commits but it didn’t fix it for us. It seems to work if we load all the landscape proxy actors at the same time, but if we load just a couple of them, the hashes change and they become non-nanite. I took some captures in renderdoc (with landscape.ForceLayersUpdate and landscape.RenderCaptureLayersNextHeightmapDraws) to see what happens on the GPU side and I think I found some problems.

When generating the heightmap, the proxies height maps are merge in a single render target, then each one are copied back to the CPU. But there seems to be some bugs that cause the textures to be misaligned when rendered in the render target, meaning that the copy that we get on CPU is wrong. If I have 2 adjacent proxies loaded, the 2 textures are rendered to the render target, the first one on top and the second one under it. But the second one is misaligned by 1 pixel and doesn’t write in the last line. Here a screenshot of renderdoc where you can see that the last line is not written to:

[Image Removed]

Also the same misalignement happens in the pass that renders the component ID (added with CL 43856326). It doesn’t write the last line.

[Image Removed]I also found some problems with the aligment of renders and copies in other passes. I started to try to fix those misalignements but I don’t know if they are expected or not. If not I think what happens is that the textures are uploaded to GPU, rendered misaligned in the render target, then copied back to CPU. Different loaded proxies will cause different overlaps, generating different hashes.

I did more debugging and noticed something wrong with one of the splines. We have splines that touch more than one landscape proxy component. For some reason I can see that one the spline doesn’t rastering all the way to the edge of the component. I did a capture and I can see that a row is missing on the edge of the 2 proxies in the spline on the left. But the spline on the right is fine and cross the edge correctly. To make sure I reverted all my changes and the commits from 5.7 and I have the same behaviour. Here’s a screenshot of the capture and what it looks like in the editor:

[Image Removed] [Image Removed]Looking at the code, the problem seems to be in the rasterization of the spline, but I didn’t find why that line is missing. Btw our landscape uses a spline layer.

For this specifically, it looks like it’s because the control point of the spline is on the border. If I move it a little further, it is applied correctly. If I move it back on the border, the spike reappear.

Thanks! I’ll keep an eye out for landscape bugs in the game. Our level artists didn’t raise any issues yet so I’m hoping the actual landscape data is not affected. And the landscape is still Nanite in packaged build.

Will you link this thread in the bug report?

Epic should be aware of this thread since we always link the specific UDN case when making the bug report. I’ve also added specific comments to our private correspondence that this thread should be checked due to the extra debugging information you provided.

You can check the Nanite visualization in builds by setting r.ForceDebugViewModes=1 in your defaultEngine.ini and setting `r.Nanite.Visualize Overview` in the console in a Development build. This should let you check that this is working in packaged builds.

Regards,

Lance Chaney

Hey David (long time no see :wink:!),

Thanks for your investigation and repro case, that’s just what we needed to find out the source of the issue, which was indeed due to a spline segment lying exactly at the border of 2 landscape streaming proxies. In the end, it came down to an incorrect (off-by-one) computation error when rasterizing landscape splines and I’ve submitted a fix for this here. Please note that a number of proxies will likely need to be re-saved in order to have the proper value on the edge but after loading every proxy individually as well as random combinations of them, I couldn’t repro the issue so I believe it’s the proper fix.

Please let me know how it goes,

Cheers!

Jo

FYI, out-of-date Nanite are rebuilt on cook since it can be done on the CPU (unlike landscape layers merge), so that explains why it’s always working on a packaged build. But of course, it’s not ideal to have this warning in the editor (plus building Nanite meshes on save slows down the save operation), so hopefully the fix will resolve that.

Let us know how it goes.