PCGFastGeoInterop ignores soft release — runtime-gen shallow refresh destroys FastGeo primitives immediately

Summary

UPCGManagedFastGeoContainer::Release calls UFastGeoContainer::DestroyRuntime unconditionally, ignoring its bHardRelease parameter. This breaks the PCG
runtime-generation “shallow refresh” contract, which relies on soft release to
keep generated visuals alive until regeneration replaces them.

As a result, with pcg.RuntimeGeneration.ISM.ComponentlessPrimitives 1 (the
FastGeo path), any invalidation of a runtime-gen cell makes all FastGeo-spawned
geometry in that cell vanish for the entire async regeneration window (~0.5–2s)
and pop back when it completes. The component-ISM path (ComponentlessPrimitives 0) does not have this problem: it marks the PISMC unused and reuses it in place (GetOrCreateManagedProceduralISMC), so the old instances keep rendering.

What Type of Bug are you experiencing?

World Creation Tools

Steps to Reproduce

  1. Set up a runtime-gen PCG graph with a GPU static-mesh spawner and FastGeo
    interop enabled (pcg.RuntimeGeneration.ISM.ComponentlessPrimitives 1).
  2. Let a cell generate so FastGeo instances are visible.
  3. Trigger a shallow refresh of that generated cell — dirty the runtime-gen
    execution source with a render-state-only change (e.g. a tracked landscape
    edit; anything that reaches DirtyRuntimeGenExecutionSources such that the
    scheduler issues Cleanup with bReleaseManagedResources = false).
  4. Observe: all FastGeo instances in the cell disappear for the whole
    regeneration, then reappear.
  5. Set pcg.RuntimeGeneration.ISM.ComponentlessPrimitives 0 and repeat - the
    same refresh keeps the old instances visible throughout (correct behavior).

Expected Result

A shallow refresh (bReleaseManagedResources = false) should mark the FastGeo container unused and keep it rendering until regeneration completes, matching the component-ISM path. UPCGManagedResource::Release(false) already does exactly this (marks unused, returns false); the FastGeo override bypasses it.

Observed Result

The FastGeo container is destroyed immediately on every shallow refresh, so the cell
goes bare for the full async regen window.

Affects Versions

5.8

Platform(s)

Windows

Additional Notes

Root Cause

UPCGManagedFastGeoContainer::Release does not branch on bHardRelease - it always calls DestroyRuntime, whereas the base UPCGManagedResource::Release treats a false argument as “mark unused, keep alive.”

Fixing Release alone is insufficient: nothing sweeps the soft-released resources afterwards - UPCGRuntimeGenExecutionSource’s post-generation task never calls CleanupUnusedManagedResources (only OnGraphExecutionAborted does), so honoring soft release without a post-generation sweep leaks the old containers. FPCGPrimitiveFactoryFastGeoPISMC (or the post-generation task) needs to release superseded containers once the replacement’s render state is created.

Workaround

I forked the plugin project-locally and made two changes:

  1. Release defers to Super::Release on the soft path and only DestroyRuntimes on the hard path;
  2. the FastGeo primitive factory arms a game-thread ticker that calls
    CleanupUnusedManagedResources once the replacement container is renderable (registered + valid instance-data offsets) plus a short grace.

This makes the FastGeo path flash-free and matches the component-ISM path.

Happy to share the diff if useful :blush: