Grass (HISM Component placed via PCG) flickers when a nearby actor visibility changes

The grass rendered by the HISM component flickers on and off when a nearby actor visibility changes and teleports. It seems that rebuilding the tree at runtime does fix the issue temporarily but the issue persists even after running the command (both outside PIE and during PIE).

There seemed to be a similar issue (linked below) that also causes the HISM to not render and it seems like it’s from a certain commit (linked below) which changed how the build async result gets applied to the game thread.

Note that clearing PCG link (generating a PCG Stamp) still has the same issue so PCG doesn’t seem to be the root of the problem

Commit:

Reverting dffdccfd8f867ee54833e135ea08ca7ae1c61210 seems to fix it but does not seem safe to do so.

Similar issue:

https://forums.unrealengine.com/t/hierarchical-instanced-static-mesh-hism-not-rendering-in-5-5-1-until-editor-ui-is-clicked/2233601

Steps to Reproduce

  • Using PCG create a large patch of grass.
  • PIE and have a character actor teleport between two places
  • The delay also effects if the bug is reproducible or not (if the delay is too big the issue doesn’t happen)
  • Running foliage.RebuildFoliageTrees at runtime fixes the issue

The issue only happens to only a few of our HISM components. Possibly related to the instance count/bounds of the HISM component

Notes from debuging with AI:

The issue appears to stem from a bug or race condition in the asynchronous building and updating of the cluster tree for the affected Hierarchical Instanced Static Mesh (HISM) components within the AInstancedFoliageActor. This is similar to a known regression in Unreal Engine 5.5 and later, where dynamically managed HISMs fail to render properly until a forced update (like a UI interaction or explicit rebuild) applies the async build results to the rendering thread.

Here’s why this fits all the observations:

  • Cluster tree build and update process: The BuildTreeIfOutdated function (called within Traverse) can run asynchronously by default. If the build is scheduled but its results aren’t immediately applied to the render state (due to timing issues in the game thread or render thread synchronization), the HISM can add mesh elements to the collector (explaining why Traverse runs and “adds instances”), but the GPU instance buffers or draw commands end up empty or invalid, resulting in no trace in RenderDoc.
  • Tie to AI visibility toggle: Toggling visibility on the AI character actor triggers a scene update (e.g., primitive registration/deregistration, bounds dirtying, or view recalculation). This can invalidate or interrupt the async cluster tree application for susceptible HISMs, exposing the sync bug. The immediate effect matches the instant hide/show behavior, as it’s not corruption but a runtime thread coordination failure.
  • Why only 2 out of 20 HISMs? These specific components likely have unique properties (e.g., higher instance count, different LOD setups, custom per-instance data, or material complexity) that make their async builds longer or more prone to the timing issue. Global effects are ruled out since it’s not affecting all foliage.
  • RebuildFoliageTrees fixes it temporarily: This command iterates over all UHierarchicalInstancedStaticMeshComponents, forces BuildTreeIfOutdated (potentially synchronously or with a force flag), and calls MarkRenderStateDirty to ensure render resources are recreated and updated on the GPU. However, since the underlying sync bug persists, the issue recurs on editor restart or level reload (as async builds aren’t persisted reliably).
  • No direct relationship needed: The AI doesn’t need a parent-child or blueprint link; its visibility change simply acts as a trigger for scene-wide updates that hit the buggy async path indirectly.
  • Alignment with ruled-out theories: This isn’t culling (Traverse still runs), corruption (immediate, not gradual), show flags/materials (component-specific, fixed without recompiles), or global rendering (only 2 affected). Bounds remain intact because the tree structure itself isn’t altered—it’s the application of build results that’s failing.

Grass doesn’t render in certain conditions (depending on AI character visibility)

RenderDoc shows no trace of grass when it’s not rendered

traverse function in FHierarchicalStaticMeshSceneProxy::GetDynamicMeshElement always gets called even when grass fails to show up

User wants to know what prevents mesh from showing up even if it’s added by GetDynamicMeshElements

Key Observations Made:

foliage.RebuildFoliageTrees command fixes the issue - this was the crucial clue

Only 2 out of 20 HISM components are affected - not a global issue

Directly tied to AI character actor visibility - toggle visibility = instant hide/show grass

The 2 affected HISM components are completely unrelated to the AI character - no parent-child relationship

Traverse function is called and adds instances - but nothing renders in RenderDoc

HISMCOcclusionBounds don’t change when issue occurs - bounds are intact

Theories We Investigated and Ruled Out:

:cross_mark: Theory 1: Engine Show Flags Issue

Premise: View->Family->EngineShowFlags.InstancedGrass was being disabled

What we tested: Added logging in GetViewRelevance to check show flags

Why it’s wrong: Only 2 out of 20 HISM components affected, not all grass globally

Evidence: Global show flags would affect all grass, not just specific components

:cross_mark: Theory 2: HISM Data Structure Corruption

Premise: InstanceReorderTable, NumBuiltInstances, or ClusterTree corruption

What we tested: Added debugging in BuildTreeIfOutdated to check data integrity

Why it’s wrong: Issue is immediate (toggle visibility = instant hide/show), not gradual corruption

Evidence: Data corruption would require rebuilding, not instant visibility changes

:cross_mark: Theory 3: Material/Shader Compilation Issues

Premise: Grass material compilation problems or shader errors

What we tested: Checked for material compilation status and shader errors

Why it’s wrong: foliage.RebuildFoliageTrees fixes it without recompiling materials

Evidence: Shader issues wouldn’t be fixed by rebuilding the tree

:cross_mark: Theory 4: Frustum/Occlusion Culling Issues

Premise: CullNode, OcclusionResults, or frustum culling preventing rendering

What we tested: Analyzed culling logic in Traverse and CullNode functions

Why it’s wrong: User confirmed Traverse is called and adds instances, but nothing renders

Evidence: Culling issues would prevent Traverse from being called at all

:cross_mark: Theory 5: LOD/Distance Culling Issues

Premise: FinalCullDistance, LODPlanes, or distance calculations culling grass

What we tested: Examined LOD calculation logic and distance culling

Why it’s wrong: Immediate visibility toggle suggests it’s not distance-based

Evidence: Distance culling wouldn’t be instantly toggled by actor visibility

:cross_mark: Theory 6: Global Rendering Pipeline Issues

Premise: Render state, depth testing, or GPU issues affecting rendering

What we tested: Checked render state and GPU-related issues

Why it’s wrong: Only affects 2 specific HISM components, not all rendering

Evidence: Global rendering issues would affect all components, not just 2

What We Know For Certain:

:white_check_mark: Traverse is called - mesh elements are added to collector

:white_check_mark: Nothing appears in RenderDoc - elements are added but not rendered

:white_check_mark: foliage.RebuildFoliageTrees fixes it - Running this command fixes it, but when restarting the editor the issue comes back. Even saving the level doesn’t work, we have to manually call it again during runtime to make it go away temporarily.

:white_check_mark: Only 2 out of 20 HISM components affected - not global issue. There are 20 components on 1 actor. And only 2 of the 20 HISM components have this flicker issue

:white_check_mark: Directly tied to AI character actor visibility - immediate toggle effect

:white_check_mark: Components are completely unrelated to AI character - no parent-child relationship

:white_check_mark: HISMCOcclusionBounds don’t change - bounds are intact, issue is not in tree structure

Hello,

Thank you for reaching out.

I’ve been assigned this issue, and we will be looking into the HISM grass flickering for you.

Hello,

After several attempts, we could not reproduce this issue.

Can you please send us a minimal test project that demonstrates the occlusion happening incorrectly?

The guide for test projects:

[Content removed]

We are on //UE5/Private-Switch-Refactor-Partner-5.5@42104362

This is the video with the commit reverted

This is the video without any fixes

This is the project files

Hello,

We have been able to reproduce this issue on that branch and CL, but the very next CL (42848392) on that branch (an integration) seemed to solve it.

Are you able to integrate the changes to your engine?