Hierarchical Instanced Static Mesh does not support Custom Data correctly

New to 4.25 was the ability to specify per instance custom float data.
It appears to work great for Instanced mesh, but for Hierarchical Instanced Static Mesh the indices are not stable. As the camera moves around the custom data moves to different instances.

I have submitted a bug report but I have not heard anything.

I found this link showing someone reporting the same problem but no answers yet.

This is the PR where support for Custom Data was added:
https://github.com/EpicGames/UnrealEngine/commit/0206745a803be82e3f131b307166158a24c89e85

I have spent some hours looking into the problem and I suspect that the problem is that the Custom Data values are indexed in the shader (MaterialTemplate.ush) directly, not via the InstanceReorderTable (which is not GPU visible anyway). I’m not sure of the best way to fix this as I don’t really understand how the HISM system works with the Tree rebuilding and instance reordering.

I submitted a bug report a few weeks ago but it has not been accepted yet. It’s easy to recreate the problem with a simple BP creation script. Sample project can be downloaded here.

===============================
UPDATE: I received an email from Epic stating this has been fixed and should be released in 4.26.

I think this is the commit.

And, due to the way indexing works, once you start messing with them, things behave as expected:

Image from Gyazo

And this will not play well with LODs - each one manages their own instances indexes. Culling with a single LOD almost works:

Image from Gyazo


Is this the bug you were referring to:

Is this the behaviour you’re after?

Image from Gyazo

Updating from BPs:

Image from Gyazo

Can you confirm there’s only a single LOD here at play for this particular mesh:

308339-annotation-2020-07-28-165752.jpg

Scratch that. It actually makes sense.

One of the advantages of HISMs is per instance culling. As soon as they leave frustum, they’re culled - you’re experiencing the same as my almost working blue sphere animation.

Not sure whether this is a bug or HISM limitation :expressionless:


It seems to affected by the way the instances are originally ordered:

Image from Gyazo

As can be seen above - one of the sides is barely affected as those indexes are neatly ordered ascending.

Thanks, yes that IS the behaviour I want but it doesn’t work for me. I have a very similar setup.
BP:

Sphere Mat:

If all the spheres are on screen it works fine. As some move off screen the sphere colours start changing. It might be LOD related but I’ve not set anything up regarding LOD.

308319-hism-bug.gif

I don’t think it’s a limitation of the HISM per se. For example the transforms work just fine. When instances are re-ordered due to culling etc, they are updated in the InstanceReorderTable in the HISM object (I think). If the custom data could be indexed via this table on GPU I think it would fix it.

Unfortunately the InstanceReorderTable is not accessible on GPU.

Another way would be to update the entire Custom data GPU copy every time the instances were reordered, and keep the GPU data in the final instance order including the reordering.

Until this works, the per instance custom data is pretty much useless for HISM.

Until this works, the per instance
custom data is pretty much useless for
HISM.

I have little to add here, unfortunately. I was under the impression this worked OK (since I needed no culling) until I started adding custom LODs last week and… yeah, you know the rest.

Adding an answer to say I received an email from Epic in response to a bug report I submitted, stating this has been fixed and should be released in 4.26.

is this fixed in 4.26 ??

As of October 1st, 2021 this still doesn’t work, just a note in case you’ve arrived via Google to this old thread.

In 4.27 I have this same problem.

1 Like

In UE5 the solution to this is to use Instanced Static Mesh, and enable Nanite.

1 Like

January 2023, 4.27.2

Thought I’d reply to this to say that in 4.27.2 I don’t have the issue.
Setting up a test in a new project appears to work fine.

This .gif (sorry about the quality here) shows a HISM with 1 million cube instances, each with their PerInstanceCustomData value set to the instance’s index. So the first instance has a custom value of 0.0, the next has 1.0, etc., up to 1 million.

Culling distances are 0 and 3000.

I created the HISM in C++ however, not in a blueprint.
I didn’t need to use BuildTreeIfOutdated() at all, as I’ve seen some other searches mention.

I do find an issue with the precision of my PerInstanceCustomData values getting worse as they get larger. I found this occurs in 2 places:

  • in the VertexInterpolator node output
  • and in the inner workings of the DebugFloat3Values node

I used Floor(myValue + 0.3f) on the VertexInterpolator output to deal with most of the imprecision, but did nothing to whatever Debug3FloatValues is doing with its input. So as you can see, imprecision is still a thing in the example (as those decimal values climb higher), but I believe this isn’t connected to the HISM custom data/thread topic.

HISMTest2

void ABasicDMIDisplayer::AddManyInstances()
{
    float xSpace = 200.0f;                                  // Spacing between cubes
    float ySpace = 125.0f;                                  // Spacing between cubes
    float zHeight = 125.0f;                                 // Height above the HISM's origin
    float toSubtract = InstanceDimension * ySpace / 2.0f;   // Start the cube rows far to the left

    for (int32 x = 0; x < InstanceDimension; ++x)           // InstanceDimension set to 1000
    {                                                       // thus giving 1 million instances.
        for (int32 y = 0; y < InstanceDimension; ++y)
        {
            FTransform transform = FTransform(FRotator(0.0f), FVector(x * xSpace, (y * ySpace) - toSubtract, zHeight));
            HISMComponent->AddInstance(transform);
            HISMComponent->SetCustomDataValue(x * InstanceDimension + y, 0, x * InstanceDimension + y);
        }
    }

    //HISMComponent->BuildTreeIfOutdated(true, false);  // Didn't need to use this.
    HISMComponent->MarkRenderStateDirty();
}