UInstancedStaticMeshComponent RemoveInstance with PerInstanceCustomData Bug

When calling RemoveInstance(index) on a UInstancedStaticMeshComponent - the instance is properly removed, however the values provided via SetCustomDataValue(...) are not properly updated as well.

Question:

Is there something else I need to do to update existing instance custom data after I remove instances or is this a bug in 4.25 (which introduced `SetCustomDataValue(...)`)?

Background:

The use case is that I have a variable number of instances (10K - 500K), each with a custom data value (set of 3 floats) which I use in the material for the ISM (and I have set the material to be used with ISM). Periodically I receive new data which I then use to update the existing ISM instances (transforms, custom data) and either add new ISM instances or remove extra ISM instances that are no longer needed. When updating existing instances or adding new instances everything works fine. However, when I call RemoveInstance(index) to remove instances I no longer need, the remaining instances have their custom data corrupted.

Example:

Bad Data (when I re-use instances and have to call `RemoveInstance`):

308450-bad-ism-data-corruption.png

Good Data (when either the number of instances stabilize or when I don't re-use instances):

308461-good-ism-data.png

Here is the code I’m using to perform the update of the ISM:

      if (onlyNewVoxels) {
        // if we want to remove all instances
        voxelMesh->ClearInstances();
      }

      FTransform t;
      FVector scale{ voxelSize, voxelSize, voxelSize };
      bool teleportUpdate = true;
      bool setRenderStateDirty = true;
      bool transformIsWorldSpace = true;
      // update the instances here
      size_t index = 0;
      if (!onlyNewVoxels) {
        UE_LOG(LogTemp, Log, TEXT("Updating %d existing instances"), voxelMesh->GetInstanceCount());
        // update voxels we already have
        for (; index < voxelMesh->GetInstanceCount(); index++) {
          // break if we have more voxels than we currently have meshes
          if (index >= numVoxels) break;
          // set the transform
          t.SetTranslationAndScale3D(voxelLocations[index], scale);
          voxelMesh->UpdateInstanceTransform(index, t,
                                             transformIsWorldSpace,
                                             setRenderStateDirty,
                                             teleportUpdate);
          if (showVoxelColor) {
            // set the color
            auto color = voxelColors[index];
            voxelMesh->SetCustomDataValue(index, 0, color.R, setRenderStateDirty);
            voxelMesh->SetCustomDataValue(index, 1, color.G, setRenderStateDirty);
            voxelMesh->SetCustomDataValue(index, 2, color.B, setRenderStateDirty);
          }
        }
      }
      // add new voxels if we have more than we have meshes
      UE_LOG(LogTemp, Log, TEXT("Adding %d new instances"), numVoxels - index);
      for (; index < numVoxels; index++) {
        // create the new mesh instance
        t.SetTranslationAndScale3D(voxelLocations[index], scale);
        voxelMesh->AddInstanceWorldSpace(t);
        if (showVoxelColor) {
          // set the color
          auto color = voxelColors[index];
          voxelMesh->SetCustomDataValue(index, 0, color.R, setRenderStateDirty);
          voxelMesh->SetCustomDataValue(index, 1, color.G, setRenderStateDirty);
          voxelMesh->SetCustomDataValue(index, 2, color.B, setRenderStateDirty);
        }
      }
      // remove voxels we don't need anymore
      UE_LOG(LogTemp, Log, TEXT("Removing %d unneeded instances"), voxelMesh->GetInstanceCount() - index);
      for (size_t i=voxelMesh->GetInstanceCount()-1; i > index ; i--) {
        voxelMesh->RemoveInstance(i);
      }
      // now mark the render state as dirty!
      voxelMesh->MarkRenderStateDirty();

This is a bug which I think has been fixed in 4.25.3

I have the exact same issue and am trying to make a minimal working (well, breaking) example on a new project to submit a bug report, if you haven’t already done so.

EDIT: I just tried it under the newest version of 4.25 and do not have the issue anymore!